home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
EuroCD 3
/
EuroCD 3.iso
/
Programming
/
PPCbwb111
/
source
/
bwb_prn.c
< prev
next >
Wrap
C/C++ Source or Header
|
1998-06-24
|
40KB
|
1,724 lines
/***************************************************************
bwb_prn.c Print Commands
for Bywater BASIC Interpreter
Copyright (c) 1992, Ted A. Campbell
Bywater Software
P. O. Box 4023
Duke Station
Durham, NC 27706
email: tcamp@acpub.duke.edu
Copyright and Permissions Information:
All U.S. and international copyrights are claimed by the
author. The author grants permission to use this code
and software based on it under the following conditions:
(a) in general, the code and software based upon it may be
used by individuals and by non-profit organizations; (b) it
may also be utilized by governmental agencies in any country,
with the exception of military agencies; (c) the code and/or
software based upon it may not be sold for a profit without
an explicit and specific permission from the author, except
that a minimal fee may be charged for media on which it is
copied, and for copying and handling; (d) the code must be
distributed in the form in which it has been released by the
author; and (e) the code and software based upon it may not
be used for illegal activities.
***************************************************************/
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <string.h>
#include <math.h>
#include "bwbasic.h"
#include "bwb_mes.h"
/* Prototypes for functions visible only to this file */
static int prn_cr( char *buffer, FILE *f );
static int prn_col = 1;
static int prn_width = 80; /* default width for stdout */
static struct bwb_variable * bwb_esetovar( struct exp_ese *e );
struct prn_fmt
{
int type; /* STRING, DOUBLE, SINGLE, or INTEGER */
int exponential; /* TRUE = use exponential notation */
int right_justified; /* TRUE = right justified else left justified */
int width; /* width of main section */
int precision; /* width after decimal point */
int commas; /* use commas every three steps */
int sign; /* prefix sign to number */
int money; /* prefix money sign to number */
int fill; /* ASCII value for fill character, normally ' ' */
int minus; /* postfix minus sign to number */
};
static struct prn_fmt *get_prnfmt( char *buffer, int *position, FILE *f );
static int bwb_xerror( char *message );
static int xxputc( FILE *f, char c );
/***************************************************************
FUNCTION: bwb_print()
DESCRIPTION: This function implements the BASIC PRINT
command.
***************************************************************/
struct bwb_line *
bwb_print( struct bwb_line *l )
{
FILE *fp;
static int pos;
int req_devnumber;
struct exp_ese *v;
static char *s_buffer; /* small, temporary buffer */
static int init = FALSE;
#if INTENSIVE_DEBUG
sprintf( bwb_ebuf, "in bwb_print(): enter function" );
bwb_debug( bwb_ebuf );
#endif
/* initialize buffers if necessary */
if ( init == FALSE )
{
init = TRUE;
if ( ( s_buffer = calloc( MAXSTRINGSIZE + 1, sizeof(char) ) ) == NULL )
{
bwb_error( err_getmem );
}
}
/* advance beyond whitespace and check for the '#' sign */
adv_ws( l->buffer, &( l->position ) );
if ( l->buffer[ l->position ] == '#' )
{
++( l->position );
adv_element( l->buffer, &( l->position ), s_buffer );
pos = 0;
v = bwb_exp( s_buffer, FALSE, &pos );
adv_ws( l->buffer, &( l->position ) );
if ( l->buffer[ l->position ] == ',' )
{
++( l->position );
}
else
{
#if PROG_ERRORS
bwb_error( "in bwb_print(): no comma after #n" );
#else
bwb_error( err_syntax );
#endif
l->next->position = 0;
return l->next;
}
req_devnumber = exp_getival( v );
/* check the requested device number */
if ( ( req_devnumber < 0 ) || ( req_devnumber >= DEF_DEVICES ))
{
#if PROG_ERRORS
bwb_error( "in bwb_input(): Requested device number is out of range." );
#else
bwb_error( err_devnum );
#endif
l->next->position = 0;
return l->next;
}
if (( dev_table[ req_devnumber ].mode == DEVMODE_CLOSED ) ||
( dev_table[ req_devnumber ].mode == DEVMODE_AVAILABLE ))
{
#if PROG_ERRORS
bwb_error( "in bwb_input(): Requested device number is not open." );
#else
bwb_error( err_devnum );
#endif
l->next->position = 0;
return l->next;
}
if ( dev_table[ req_devnumber ].mode != DEVMODE_OUTPUT )
{
#if PROG_ERRORS
bwb_error( "in bwb_print(): Requested device is not open for OUTPUT." );
#else
bwb_error( err_devnum );
#endif
l->next->position = 0;
return l->next;
}
#if INTENSIVE_DEBUG
sprintf( bwb_ebuf, "in bwb_print(): device number is <%d>",
req_devnumber );
bwb_debug( bwb_ebuf );
#endif
/* look up the requested device in the device table */
fp = dev_table[ req_devnumber ].cfp;
}
else
{
fp = stdout;
}
bwb_xprint( l, fp );
l->next->position = 0;
return l->next;
}
/***************************************************************
FUNCTION: bwb_xprint()
DESCRIPTION:
***************************************************************/
int
bwb_xprint( struct bwb_line *l, FILE *f )
{
struct exp_ese *e;
int loop;
static int p;
static int fs_pos;
struct prn_fmt *format;
static char *format_string;
static char *output_string;
static char *element;
static char *prnbuf;
static int init = FALSE;
#if INTENSIVE_DEBUG || TEST_BSTRING
bstring *b;
#endif
/* initialize buffers if necessary */
if ( init == FALSE )
{
init = TRUE;
if ( ( format_string = calloc( MAXSTRINGSIZE + 1, sizeof(char) ) ) == NULL )
{
bwb_error( err_getmem );
}
if ( ( output_string = calloc( MAXSTRINGSIZE + 1, sizeof(char) ) ) == NULL )
{
bwb_error( err_getmem );
}
if ( ( element = calloc( MAXSTRINGSIZE + 1, sizeof(char) ) ) == NULL )
{
bwb_error( err_getmem );
}
if ( ( prnbuf = calloc( MAXSTRINGSIZE + 1, sizeof(char) ) ) == NULL )
{
bwb_error( err_getmem );
}
}
/* Detect USING Here */
fs_pos = -1;
/* get "USING" in format_string */
p = l->position;
adv_element( l->buffer, &p, format_string );
bwb_strtoupper( format_string );
/* check to be sure */
if ( strcmp( format_string, "USING" ) == 0 )
{
l->position = p;
adv_ws( l->buffer, &( l->position ) );
/* now get the format string in format_string */
e = bwb_exp( l->buffer, FALSE, &( l->position ) );
if ( e->type == STRING )
{
/* copy the format string to buffer */
str_btoc( format_string, exp_getsval( e ) );
/* look for ';' after format string */
fs_pos = 0;
adv_ws( l->buffer, &( l->position ) );
if ( l->buffer[ l->position ] == ';' )
{
++l->position;
adv_ws( l->buffer, &( l->position ) );
}
else
{
#if PROG_ERRORS
bwb_error( "Failed to find \";\" after format string in PRINT USING" );
#else
bwb_error( err_syntax );
#endif
return FALSE;
}
#if INTENSIVE_DEBUG
sprintf( bwb_ebuf, "in bwb_xprint(): Found USING, format string <%s>",
format_string );
bwb_debug( bwb_ebuf );
#endif
}
else
{
#if PROG_ERRORS
bwb_error( "Failed to find format string after PRINT USING" );
#else
bwb_error( err_syntax );
#endif
return FALSE;
}
}
/* if no arguments, simply print CR and return */
adv_ws( l->buffer, &( l->position ) );
switch( l->buffer[ l->position ] )
{
case '\0':
case '\n':
case '\r':
case ':':
xprintf( f, "\n" );
return TRUE;
default:
break;
}
/* LOOP THROUGH PRINT ELEMENTS */
loop = TRUE;
while( loop == TRUE )
{
/* resolve the string */
e = bwb_exp( l->buffer, FALSE, &( l->position ) );
#if INTENSIVE_DEBUG
sprintf( bwb_ebuf, "in bwb_xprint(): op <%d> type <%c>",
e->operation, e->type );
bwb_debug( bwb_ebuf );
#endif
/* an OP_NULL probably indicates a terminating ';', but this
will be detected later, so we can ignore it for now */
if ( e->operation != OP_NULL )
{
#if TEST_BSTRING
b = exp_getsval( e );
sprintf( bwb_ebuf, "in bwb_xprint(): bstring name is <%s>",
b->name );
bwb_debug( bwb_ebuf );
#endif
str_btoc( element, exp_getsval( e ) );
}
else
{
element[ 0 ] = '\0';
}
#if INTENSIVE_DEBUG
sprintf( bwb_ebuf, "in bwb_xprint(): element <%s>",
element );
bwb_debug( bwb_ebuf );
#endif
/* print with format if there is one */
if (( fs_pos > -1 ) && ( strlen( element ) > 0 ))
{
format = get_prnfmt( format_string, &fs_pos, f );
#if INTENSIVE_DEBUG
sprintf( bwb_ebuf, "in bwb_xprint(): format type <%c> width <%d>",
format->type, format->width );
bwb_debug( bwb_ebuf );
#endif
switch( format->type )
{
case STRING:
if ( e->type != STRING )
{
#if PROG_ERRORS
bwb_error( "Type mismatch in PRINT USING" );
#else
bwb_error( err_mismatch );
#endif
}
sprintf( output_string, "%.*s", format->width,
element );
#if INTENSIVE_DEBUG
sprintf( bwb_ebuf, "in bwb_xprint(): output string <%s>",
output_string );
bwb_debug( bwb_ebuf );
#endif
xprintf( f, output_string );
break;
case INTEGER:
if ( e->type == STRING )
{
#if PROG_ERRORS
bwb_error( "Type mismatch in PRINT USING" );
#else
bwb_error( err_mismatch );
#endif
}
sprintf( output_string, "%.*d", format->width,
exp_getival( e ) );
xprintf( f, output_string );
break;
case SINGLE:
case DOUBLE:
if ( e->type == STRING )
{
#if PROG_ERRORS
bwb_error( "Type mismatch in PRINT USING" );
#else
bwb_error( err_mismatch );
#endif
}
if ( format->exponential == TRUE )
{
sprintf( output_string, "%.le",
e->dval );
xprintf( f, output_string );
}
else
{
sprintf( output_string, "%*.*lf",
format->width + 1 + format->precision,
format->precision, e->dval );
xprintf( f, output_string );
}
break;
default:
#if PROG_ERRORS
sprintf( bwb_ebuf, "in bwb_xprint(): get_prnfmt() returns unknown type <%c>",
format->type );
bwb_error( bwb_ebuf );
#else
bwb_error( err_mismatch );
#endif
break;
}
}
/* not a format string: use defaults */
else if ( strlen( element ) > 0 )
{
switch( e->type )
{
case STRING:
xprintf( f, element );
break;
case INTEGER:
sprintf( prnbuf, " %d", exp_getival( e ) );
xprintf( f, prnbuf );
break;
case DOUBLE:
sprintf( prnbuf, " %.*f", prn_precision( bwb_esetovar( e )),
exp_getdval( e ) );
xprintf( f, prnbuf );
break;
default:
sprintf( prnbuf, " %.*f", prn_precision( bwb_esetovar( e )),
exp_getfval( e ) );
xprintf( f, prnbuf );
break;
}
}
/* check the position to see if the loop should continue */
adv_ws( l->buffer, &( l->position ) );
switch( l->buffer[ l->position ] )
{
case ':': /* end of line segment */
loop = FALSE;
/* ++( l->position ); */
break;
case '\0': /* end of buffer */
case '\n':
case '\r':
loop = FALSE;
break;
case ',': /* tab over */
xputc( f, '\t' );
++l->position;
adv_ws( l->buffer, &( l->position ) );
break;
case ';': /* concatenate strings */
++l->position;
adv_ws( l->buffer, &( l->position ) );
break;
}
} /* end of loop through print elements */
/* call prn_cr() to print a CR if it is not overridden by a
concluding ';' mark */
prn_cr( l->buffer, f );
return TRUE;
} /* end of function bwb_xprint() */
/***************************************************************
FUNCTION: get_prnfmt()
DESCRIPTION:
***************************************************************/
struct prn_fmt *
get_prnfmt( char *buffer, int *position, FILE *f )
{
static struct prn_fmt retstruct;
register int c;
int loop;
/* set some defaults */
retstruct.type = FALSE;
retstruct.exponential = FALSE;
retstruct.right_justified = FALSE;
retstruct.commas = FALSE;
retstruct.sign = FALSE;
retstruct.money = FALSE;
retstruct.fill = ' ';
retstruct.minus = FALSE;
/* check for negative position */
if ( *position < 0 )
{
return &retstruct;
}
/* advance past whitespace */
adv_ws( buffer, position );
/* check first character: a lost can be decided right here */
loop = TRUE;
while( loop == TRUE )
{
#if INTENSIVE_DEBUG
sprintf( bwb_ebuf, "in get_prnfmt(): loop, buffer <%s>",
&( buffer[ *position ] ) );
bwb_debug( bwb_ebuf );
#endif
switch( buffer[ *position ] )
{
case ' ': /* end of this format segment */
loop = FALSE;
break;
case '\0': /* end of format string */
case '\n':
case '\r':
*position = -1;
return &retstruct;
case '_': /* print next character as literal */
++( *position );
xputc( f, buffer[ *position ] );
++( *position );
break;
case '!':
retstruct.type = STRING;
retstruct.width = 1;
return &retstruct;
case '\\':
#if INTENSIVE_DEBUG
sprintf( bwb_ebuf, "in get_prnfmt(): found \\" );
bwb_debug( bwb_ebuf );
#endif
retstruct.type = STRING;
++( *position );
for ( retstruct.width = 0; buffer[ *position ] == ' '; ++( *position ) )
{
++retstruct.width;
}
if ( buffer[ *position ] == '\\' )
{
++( *position );
}
return &retstruct;
case '$':
++( *position );
retstruct.money = TRUE;
if ( buffer[ *position ] == '$' )
{
++( *position );
}
break;
case '*':
++( *position );
retstruct.fill = '*';
if ( buffer[ *position ] == '*' )
{
++( *position );
}
break;
case '+':
++( *position );
retstruct.sign = TRUE;
break;
case '#':
retstruct.type = INTEGER; /* for now */
++( *position );
for ( retstruct.width = 1; buffer[ *position ] == '#'; ++( *position ) )
{
++retstruct.width;
}
if ( buffer[ *position ] == ',' )
{
retstruct.commas = TRUE;
}
if ( buffer[ *position ] == '.' )
{
retstruct.type = DOUBLE;
++( *position );
for ( retstruct.precision = 0; buffer[ *position ] == '#'; ++( *position ) )
{
++retstruct.precision;
}
}
if ( buffer[ *position ] == '-' )
{
retstruct.minus = TRUE;
++( *position );
}
return &retstruct;
case '^':
retstruct.type = DOUBLE;
retstruct.exponential = TRUE;
for ( retstruct.width = 1; buffer[ *position ] == '^'; ++( *position ) )
{
++retstruct.width;
}
return &retstruct;
}
} /* end of loop */
return &retstruct;
}
/***************************************************************
FUNCTION: prn_cr()
DESCRIPTION:
***************************************************************/
static int
prn_cr( char *buffer, FILE *f )
{
register int c;
int loop;
/* find the end of the buffer */
for ( c = 0; buffer[ c ] != '\0'; ++c )
{
}
#if INTENSIVE_DEBUG
sprintf( bwb_ebuf, "in prn_cr(): initial c is <%d>", c );
bwb_debug( bwb_ebuf );
#endif
/* back up through any whitespace */
loop = TRUE;
while ( loop == TRUE )
{
switch( buffer[ c ] )
{
case ' ': /* if whitespace */
case '\t':
case 0:
#if INTENSIVE_DEBUG
sprintf( bwb_ebuf, "in prn_cr(): backup: c is <%d>, char <%c>[0x%x]",
c, buffer[ c ], buffer[ c ] );
bwb_debug( bwb_ebuf );
#endif
--c; /* back up */
if ( c < 0 ) /* check position */
{
loop = FALSE;
}
break;
default: /* else break out */
#if INTENSIVE_DEBUG
sprintf( bwb_ebuf, "in prn_cr(): breakout: c is <%d>, char <%c>[0x%x]",
c, buffer[ c ], buffer[ c ] );
bwb_debug( bwb_ebuf );
#endif
loop = FALSE;
break;
}
}
if ( buffer[ c ] == ';' )
{
#if INTENSIVE_DEBUG
sprintf( bwb_ebuf, "in prn_cr(): concluding <;> detected." );
bwb_debug( bwb_ebuf );
#endif
return FALSE;
}
else
{
xprintf( f, "\n" );
return TRUE;
}
}
/***************************************************************
FUNCTION: xprintf()
DESCRIPTION:
***************************************************************/
int
xprintf( FILE *f, char *buffer )
{
char *p;
/* DO NOT try anything so stupid as to run bwb_debug() from
here, because it will create an endless loop. And don't
ask how I know. */
for ( p = buffer; *p != '\0'; ++p )
{
xputc( f, *p );
}
return TRUE;
}
/***************************************************************
FUNCTION: xputc()
DESCRIPTION:
***************************************************************/
int
xputc( FILE *f, char c )
{
static int tab_pending = FALSE;
register int i;
/* check for pending TAB */
if ( tab_pending == TRUE )
{
if ( (int) c < ( * prn_getcol( f ) ) )
{
xxputc( f, '\n' );
}
while( ( * prn_getcol( f )) < (int) c )
{
xxputc( f, ' ' );
}
tab_pending = FALSE;
return TRUE;
}
/* check c for specific output options */
switch( c )
{
case PRN_TAB:
tab_pending = TRUE;
break;
case '\t':
while( ( (* prn_getcol( f )) % 14 ) != 0 )
{
xxputc( f, ' ' );
}
break;
default:
xxputc( f, c );
break;
}
return TRUE;
}
/***************************************************************
FUNCTION: xxputc()
DESCRIPTION:
***************************************************************/
int
xxputc( FILE *f, char c )
{
/* check to see if width has been exceeded */
if ( * prn_getcol( f ) >= prn_getwidth( f ))
{
fputc( '\n', f ); /* output LF */
* prn_getcol( f ) = 1; /* and reset */
}
/* adjust the column counter */
if ( c == '\n' )
{
* prn_getcol( f ) = 1;
}
else
{
++( * prn_getcol( f ));
}
/* now output the character */
return fputc( c, f );
}
/***************************************************************
FUNCTION: prn_getcol()
DESCRIPTION:
***************************************************************/
int *
prn_getcol( FILE *f )
{
register int n;
static int dummy_pos;
if (( f == stdout ) || ( f == stderr ))
{
return &prn_col;
}
for ( n = 0; n < DEF_DEVICES; ++n )
{
if ( dev_table[ n ].cfp == f )
{
return &( dev_table[ n ].col );
}
}
/* search failed */
#if PROG_ERRORS
bwb_error( "in prn_getcol(): failed to find file pointer" );
#else
bwb_error( err_devnum );
#endif
return &dummy_pos;
}
/***************************************************************
FUNCTION: prn_getwidth()
DESCRIPTION:
***************************************************************/
int
prn_getwidth( FILE *f )
{
register int n;
if (( f == stdout ) || ( f == stderr ))
{
return prn_width;
}
for ( n = 0; n < DEF_DEVICES; ++n )
{
if ( dev_table[ n ].cfp == f )
{
return dev_table[ n ].width;
}
}
/* search failed */
#if PROG_ERRORS
bwb_error( "in prn_getwidth(): failed to find file pointer" );
#else
bwb_error( err_devnum );
#endif
return 1;
}
/***************************************************************
FUNCTION: prn_precision()
DESCRIPTION:
***************************************************************/
int
prn_precision( struct bwb_variable *v )
{
int max_precision = 6;
double dval, d;
int r;
/* check for double value */
if ( v->type == DOUBLE )
{
max_precision = 12;
}
/* get the value in dval */
dval = var_getdval( v );
/* cycle through until precision is found */
d = 1.0;
for ( r = 0; r < max_precision; ++r )
{
#if INTENSIVE_DEBUG
sprintf( bwb_ebuf, "in prn_precision(): fmod( %f, %f ) = %.12f",
dval, d, fmod( dval, d ) );
bwb_debug( bwb_ebuf );
#endif
if ( fmod( dval, d ) < 0.0000001 )
{
return r;
}
d /= 10;
}
/* return */
return r;
}
/***************************************************************
FUNCTION: fnc_tab()
DESCRIPTION:
***************************************************************/
struct bwb_variable *
fnc_tab( int argc, struct bwb_variable *argv )
{
static struct bwb_variable nvar;
static int init = FALSE;
static char t_string[ 4 ];
static char nvar_name[] = "(tmp)";
bstring *b;
/* initialize nvar if necessary */
if ( init == FALSE )
{
init = TRUE;
var_make( &nvar, (int) STRING );
/* nvar.name = nvar_name; */
}
/* check for correct number of parameters */
if ( argc < 1 )
{
#if PROG_ERRORS
sprintf( bwb_ebuf, "Not enough parameters (%d) to function TAB().",
argc );
bwb_error( bwb_ebuf );
#else
bwb_error( err_syntax );
#endif
break_handler();
return NULL;
}
else if ( argc > 1 )
{
#if PROG_ERRORS
sprintf( bwb_ebuf, "Too many parameters (%d) to function TAB().",
argc );
bwb_error( bwb_ebuf );
#else
bwb_error( err_syntax );
#endif
break_handler();
return NULL;
}
t_string[ 0 ] = PRN_TAB;
t_string[ 1 ] = (char) var_getival( &( argv[ 0 ] ));
t_string[ 2 ] = '\0';
b = var_getsval( &nvar );
str_ctob( b, t_string );
return &nvar;
}
/***************************************************************
FUNCTION: fnc_spc()
DESCRIPTION:
***************************************************************/
struct bwb_variable *
fnc_spc( int argc, struct bwb_variable *argv )
{
return fnc_space( argc, argv );
}
/***************************************************************
FUNCTION: fnc_space()
DESCRIPTION:
***************************************************************/
struct bwb_variable *
fnc_space( int argc, struct bwb_variable *argv )
{
static struct bwb_variable nvar;
static char *tbuf;
static int init = FALSE;
int spaces;
register int i;
bstring *b;
/* check for correct number of parameters */
if ( argc < 1 )
{
#if PROG_ERRORS
sprintf( bwb_ebuf, "Not enough parameters (%d) to function SPACE$().",
argc );
bwb_error( bwb_ebuf );
#else
bwb_error( err_syntax );
#endif
break_handler();
return NULL;
}
else if ( argc > 1 )
{
#if PROG_ERRORS
sprintf( bwb_ebuf, "Too many parameters (%d) to function SPACE$().",
argc );
bwb_error( bwb_ebuf );
#else
bwb_error( err_syntax );
#endif
break_handler();
return NULL;
}
/* initialize nvar if necessary */
if ( init == FALSE )
{
init = TRUE;
var_make( &nvar, (int) STRING );
if ( ( tbuf = calloc( MAXSTRINGSIZE + 1, sizeof( char ) )) == NULL )
{
bwb_error( err_getmem );
}
}
tbuf[ 0 ] = '\0';
spaces = var_getival( &( argv[ 0 ] ));
/* add spaces to the string */
for ( i = 0; i < spaces; ++i )
{
tbuf[ i ] = ' ';
tbuf[ i + 1 ] = '\0';
}
b = var_getsval( &nvar );
str_ctob( b, tbuf );
return &nvar;
}
/***************************************************************
FUNCTION: fnc_pos()
DESCRIPTION:
***************************************************************/
struct bwb_variable *
fnc_pos( int argc, struct bwb_variable *argv )
{
static struct bwb_variable nvar;
static int init = FALSE;
static char nvar_name[] = "<pos()>";
/* initialize nvar if necessary */
if ( init == FALSE )
{
init = TRUE;
var_make( &nvar, (int) INTEGER );
/* nvar.name = nvar_name; */
}
* var_findival( &nvar, nvar.array_pos ) = prn_col;
return &nvar;
}
/***************************************************************
FUNCTION: fnc_err()
DESCRIPTION:
***************************************************************/
struct bwb_variable *
fnc_err( int argc, struct bwb_variable *argv )
{
static struct bwb_variable nvar;
static int init = FALSE;
static char nvar_name[] = "<err()>";
/* initialize nvar if necessary */
if ( init == FALSE )
{
init = TRUE;
var_make( &nvar, (int) INTEGER );
/* nvar.name = nvar_name; */
}
* var_findival( &nvar, nvar.array_pos ) = err_number;
return &nvar;
}
/***************************************************************
FUNCTION: fnc_erl()
DESCRIPTION:
***************************************************************/
struct bwb_variable *
fnc_erl( int argc, struct bwb_variable *argv )
{
static struct bwb_variable nvar;
static int init = FALSE;
static char nvar_name[] = "<erl()>";
/* initialize nvar if necessary */
if ( init == FALSE )
{
init = TRUE;
var_make( &nvar, (int) INTEGER );
/* nvar.name = nvar_name; */
}
* var_findival( &nvar, nvar.array_pos ) = err_line;
return &nvar;
}
/***************************************************************
FUNCTION: bwb_debug()
DESCRIPTION: This function is called to display
debugging messages in Bywater BASIC.
It does not break out at the current
point (as bwb_error() does).
***************************************************************/
#if PERMANENT_DEBUG
int
bwb_debug( char *message )
{
char tbuf[ MAXSTRINGSIZE + 1 ];
fflush( stdout );
fflush( errfdevice );
if ( prn_col != 1 )
{
xprintf( errfdevice, "\n" );
}
sprintf( tbuf, "DEBUG %s\n", message );
xprintf( errfdevice, tbuf );
return TRUE;
}
#endif
/***************************************************************
FUNCTION: bwb_lerror()
DESCRIPTION: This function implements the BASIC ERROR
command.
***************************************************************/
struct bwb_line *
bwb_lerror( struct bwb_line *l )
{
char tbuf[ MAXSTRINGSIZE + 1 ];
int n;
#if INTENSIVE_DEBUG
sprintf( bwb_ebuf, "in bwb_lerror(): entered function " );
bwb_debug( bwb_ebuf );
#endif
/* Check for argument */
adv_ws( l->buffer, &( l->position ) );
switch( l->buffer[ l->position ] )
{
case '\0':
case '\n':
case '\r':
case ':':
bwb_error( err_incomplete );
l->next->position = 0;
return l->next;
default:
break;
}
/* get the variable name or numerical constant */
adv_element( l->buffer, &( l->position ), tbuf );
n = atoi( tbuf );
#if INTENSIVE_DEBUG
sprintf( bwb_ebuf, "in bwb_lerror(): error number is <%d> ", n );
bwb_debug( bwb_ebuf );
#endif
/* check the line number value */
if ( ( n < 0 ) || ( n >= N_ERRORS ))
{
sprintf( bwb_ebuf, "Error number %d is out of range", n );
bwb_xerror( bwb_ebuf );
return l;
}
bwb_xerror( err_table[ n ] );
return l;
}
/***************************************************************
FUNCTION: bwb_error()
DESCRIPTION: This function is called to handle errors
in Bywater BASIC. It displays the error
message, then calls the break_handler()
routine.
***************************************************************/
int
bwb_error( char *message )
{
register int e;
static char tbuf[ MAXSTRINGSIZE + 1 ]; /* must be permanent */
/* try to find the error message to identify the error number */
err_line = bwb_number; /* set error line number */
for ( e = 0; e < N_ERRORS; ++e )
{
if ( message == err_table[ e ] ) /* set error number */
{
err_number = e;
e = N_ERRORS; /* break out of loop quickly */
}
}
/* if err_gosubn is not set, then use xerror routine */
if ( err_gosubn == 0 )
{
return bwb_xerror( message );
}
/* err_gosubn is not set; call user-defined error subroutine */
sprintf( tbuf, "GOSUB %d", err_gosubn );
cnd_xpline( bwb_l, tbuf );
return TRUE;
}
/***************************************************************
FUNCTION: bwb_xerror()
DESCRIPTION: This function is called by bwb_error()
in Bywater BASIC. It displays the error
message, then calls the break_handler()
routine.
***************************************************************/
int
bwb_xerror( char *message )
{
static char tbuf[ MAXSTRINGSIZE + 1 ]; /* this memory should be
permanent in case of memory
overrun errors */
fflush( stdout );
fflush( errfdevice );
if ( prn_col != 1 )
{
xprintf( errfdevice, "\n" );
}
if ( bwb_number == 0 )
{
sprintf( tbuf, "\n%s: %s\n", ERRD_HEADER, message );
}
else
{
sprintf( tbuf, "\n%s %d: %s\n", ERROR_HEADER, bwb_number, message );
}
xprintf( errfdevice, tbuf );
break_handler();
return FALSE;
}
/***************************************************************
FUNCTION: matherr()
DESCRIPTION: This function is called to handle math
errors in Bywater BASIC. It displays
the error message, then calls the
break_handler() routine.
***************************************************************/
int
matherr( struct exception *except )
{
perror( MATHERR_HEADER );
break_handler();
return FALSE;
}
static struct bwb_variable *
bwb_esetovar( struct exp_ese *e )
{
static struct bwb_variable b;
static init = FALSE;
var_make( &b, e->type );
switch( e->type )
{
case STRING:
str_btob( var_findsval( &b, b.array_pos ), exp_getsval( e ) );
break;
case DOUBLE:
* var_finddval( &b, b.array_pos ) = e->dval;
break;
case INTEGER:
* var_findival( &b, b.array_pos ) = e->ival;
break;
default:
* var_findfval( &b, b.array_pos ) = e->fval;
break;
}
return &b;
}
/***************************************************************
FUNCTION: bwb_width()
DESCRIPTION:
***************************************************************/
struct bwb_line *
bwb_width( struct bwb_line *l )
{
int req_devnumber;
int req_width;
struct exp_ese *e;
char tbuf[ MAXSTRINGSIZE + 1 ];
int pos;
/* detect device number if present */
req_devnumber = -1;
adv_ws( l->buffer, &( l->position ) );
if ( l->buffer[ l->position ] == '#' )
{
++( l->position );
adv_element( l->buffer, &( l->position ), tbuf );
pos = 0;
e = bwb_exp( tbuf, FALSE, &pos );
adv_ws( l->buffer, &( l->position ) );
if ( l->buffer[ l->position ] == ',' )
{
++( l->position );
}
else
{
#if PROG_ERRORS
bwb_error( "in bwb_width(): no comma after #n" );
#else
bwb_error( err_syntax );
#endif
l->next->position = 0;
return l->next;
}
req_devnumber = exp_getival( e );
/* check the requested device number */
if ( ( req_devnumber < 0 ) || ( req_devnumber >= DEF_DEVICES ))
{
#if PROG_ERRORS
bwb_error( "in bwb_width(): Requested device number is out of range." );
#else
bwb_error( err_devnum );
#endif
l->next->position = 0;
return l->next;
}
#if INTENSIVE_DEBUG
sprintf( bwb_ebuf, "in bwb_width(): device number is <%d>",
req_devnumber );
bwb_debug( bwb_ebuf );
#endif
}
/* read the width requested */
e = bwb_exp( l->buffer, FALSE, &( l->position ));
req_width = exp_getival( e );
/* check the width */
if ( ( req_width < 1 ) || ( req_width > 255 ))
{
#if PROG_ERRORS
bwb_error( "in bwb_width(): Requested width is out of range (1-255)" );
#else
bwb_error( err_valoorange );
#endif
}
/* assign the width */
if ( req_devnumber == -1 )
{
prn_width = req_width;
}
else
{
dev_table[ req_devnumber ].width = req_width;
}
/* return */
return l->next;
}
/***************************************************************
FUNCTION: bwb_write()
DESCRIPTION:
***************************************************************/
struct bwb_line *
bwb_write( struct bwb_line *l )
{
struct exp_ese *e;
int req_devnumber;
int pos;
FILE *fp;
char tbuf[ MAXSTRINGSIZE + 1 ];
int loop;
static struct bwb_variable nvar;
static int init = FALSE;
/* initialize variable if necessary */
if ( init == FALSE )
{
init = TRUE;
var_make( &nvar, SINGLE );
}
/* detect device number if present */
adv_ws( l->buffer, &( l->position ) );
if ( l->buffer[ l->position ] == '#' )
{
++( l->position );
adv_element( l->buffer, &( l->position ), tbuf );
pos = 0;
e = bwb_exp( tbuf, FALSE, &pos );
adv_ws( l->buffer, &( l->position ) );
if ( l->buffer[ l->position ] == ',' )
{
++( l->position );
}
else
{
#if PROG_ERRORS
bwb_error( "in bwb_write(): no comma after #n" );
#else
bwb_error( err_syntax );
#endif
l->next->position = 0;
return l->next;
}
req_devnumber = exp_getival( e );
/* check the requested device number */
if ( ( req_devnumber < 0 ) || ( req_devnumber >= DEF_DEVICES ))
{
#if PROG_ERRORS
bwb_error( "in bwb_write(): Requested device number is out of range." );
#else
bwb_error( err_devnum );
#endif
l->next->position = 0;
return l->next;
}
if (( dev_table[ req_devnumber ].mode == DEVMODE_CLOSED ) ||
( dev_table[ req_devnumber ].mode == DEVMODE_AVAILABLE ))
{
#if PROG_ERRORS
bwb_error( "in bwb_write(): Requested device number is not open." );
#else
bwb_error( err_devnum );
#endif
l->next->position = 0;
return l->next;
}
if ( dev_table[ req_devnumber ].mode != DEVMODE_OUTPUT )
{
#if PROG_ERRORS
bwb_error( "in bwb_write(): Requested device is not open for OUTPUT." );
#else
bwb_error( err_devnum );
#endif
l->next->position = 0;
return l->next;
}
#if INTENSIVE_DEBUG
sprintf( bwb_ebuf, "in bwb_write(): device number is <%d>",
req_devnumber );
bwb_debug( bwb_ebuf );
#endif
/* look up the requested device in the device table */
fp = dev_table[ req_devnumber ].cfp;
}
else
{
fp = stdout;
}
/* be sure there is an element to print */
adv_ws( l->buffer, &( l->position ) );
loop = TRUE;
switch( l->buffer[ l->position ] )
{
case '\n':
case '\r':
case '\0':
case ':':
loop = FALSE;
break;
}
/* loop through elements */
while ( loop == TRUE )
{
/* get the next element */
e = bwb_exp( l->buffer, FALSE, &( l->position ));
/* perform type-specific output */
switch( e->type )
{
case STRING:
xputc( fp, '\"' );
str_btoc( tbuf, exp_getsval( e ) );
xprintf( fp, tbuf );
xputc( fp, '\"' );
#if INTENSIVE_DEBUG
sprintf( bwb_ebuf, "in bwb_write(): output string element <\"%s\">",
tbuf );
bwb_debug( bwb_ebuf );
#endif
break;
default:
* var_findfval( &nvar, nvar.array_pos ) =
exp_getfval( e );
sprintf( tbuf, " %.*f", prn_precision( &nvar ),
var_getfval( &nvar ) );
xprintf( fp, tbuf );
#if INTENSIVE_DEBUG
sprintf( bwb_ebuf, "in bwb_write(): output numerical element <%s>",
tbuf );
bwb_debug( bwb_ebuf );
#endif
break;
} /* end of case for type-specific output */
/* seek a comma at end of element */
adv_ws( l->buffer, &( l->position ) );
if ( l->buffer[ l->position ] == ',' )
{
xputc( fp, ',' );
++( l->position );
}
/* no comma: end the loop */
else
{
loop = FALSE;
}
} /* end of loop through elements */
/* print LF */
xputc( fp, '\n' );
/* return */
l->next->position = 0;
return l->next;
}